#include "NAND_device.h"

NAND_device::NAND_device(sc_module_name name_)
	: sc_module			(name_)
	, current_state		(COMMAND)
	, DATA_cnt			(0)
	, BUSY_cnt_program	(DEFAULT_PROGRAM_BUSY_CYCLE)
	, BUSY_cnt_read		(DEFAULT_READ_BUSY_CYCLE)
	, BUSY_cnt_erase	(DEFAULT_ERASE_BUSY_CYCLE)
	, R_B				(true)
{
	for (int i=0; i<(NAND_PAGE_SIZE*DEFAULT_PAGE_PER_BLOCK*DEFAULT_BLOCK_PER_PLANE/2); i++)
		FLASH_Array[i]=0;

	SC_THREAD(FLASH_FSM_process);
	dont_initialize();
	sensitive << CLK.pos();
}

NAND_device::~NAND_device()
{
}

const bool
NAND_device::NAND_read_R_B() const
{
	return R_B;
}

const byte
NAND_device::NAND_read() const
{
	byte return_data = 0;

	if (R_B == true)
		return_data = DATA_IO;

	return return_data;
}

void
NAND_device::NAND_write(byte receive_DATA_IO)
{
	DATA_IO = receive_DATA_IO;
}

void
NAND_device::flash_addr_gen()
{
	if ((cmd == NAND_START_PROGRAM) || (cmd == NAND_START_READ))
		flash_addr = ((row_addr1<<20) | (row_addr0<<12) | (col_addr1<<8)& 0xF | col_addr0);
	else if (cmd == NAND_START_ERASE)
		flash_addr = ((row_addr1<<20) | (row_addr0<<12));
#ifdef	DEBUG_NAND
	cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> flash_addr_gen() > Physical address : 0x" << hex << flash_addr << dec << endl;
#endif
}

void
NAND_device::FLASH_FSM_process()
{
	while (true)
	{
		switch (current_state)
		{
		case COMMAND:
			cmd = DATA_IO;
			if ((cmd == NAND_START_PROGRAM) || (cmd == NAND_START_READ))
			{
#ifdef	DEBUG_NAND
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> NAND_COMMAND : 0x" << hex << (unsigned)DATA_IO << dec << endl;
#endif
				current_state = COL_ADDR0;
			}
			else if (cmd == NAND_START_ERASE)
			{
#ifdef	DEBUG_NAND
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> NAND_COMMAND : 0x" << hex << (unsigned)DATA_IO << dec << endl;
#endif
				current_state = ROW_ADDR0;
			}
			break;
		case COL_ADDR0:
#ifdef	DEBUG_NAND
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> NAND_COL_ADDR0 : 0x" << hex << (unsigned)DATA_IO << dec << endl;
#endif
			col_addr0 = DATA_IO;
			current_state = COL_ADDR1;
			break;
		case COL_ADDR1:
#ifdef	DEBUG_NAND
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> NAND_COL_ADDR1 : 0x" << hex << (unsigned)DATA_IO << dec << endl;
#endif
			col_addr1 = DATA_IO;
			current_state = ROW_ADDR0;
			break;
		case ROW_ADDR0:
#ifdef	DEBUG_NAND
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> NAND_ROW_ADDR0 : 0x" << hex << (unsigned)DATA_IO << dec << endl;
#endif
			row_addr0 = DATA_IO;
			current_state = ROW_ADDR1;
			break;
		case ROW_ADDR1:
#ifdef	DEBUG_NAND
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> NAND_ROW_ADDR1 : 0x" << hex << (unsigned)DATA_IO << dec << endl;
#endif
			row_addr1 = DATA_IO;
			flash_addr_gen();
			if (cmd == NAND_START_PROGRAM)
				current_state = WR_DATA;
			else if (cmd == NAND_START_READ)
				current_state = CONFIRM;
			else if (cmd == NAND_START_ERASE)
				current_state = ERASE_DATA;
			break;
		case WR_DATA:
			FLASH_Array[(flash_addr>>12)+DATA_cnt] = DATA_IO;
#ifdef	DEBUG_NAND
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> FLASH_write : FLASH_Array[0x" << hex << (flash_addr>>12) << dec << "+" << DATA_cnt << "] : 0x" << hex << (unsigned)DATA_IO << dec << endl;
#endif
			DATA_cnt++;
			if (DATA_cnt == NAND_PAGE_SIZE)
			{
				current_state = CONFIRM;
				DATA_cnt = 0;
			}
			break;
		case RD_DATA:
			if (DATA_cnt != NAND_PAGE_SIZE)
			{
				DATA_IO = FLASH_Array[(flash_addr>>12)+DATA_cnt];
#ifdef	DEBUG_NAND
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> FLASH_read : 0x" << hex << (unsigned)DATA_IO << " = FLASH_Array[0x" << (flash_addr>>12) << dec << "+" << DATA_cnt << "] " <<  endl;
#endif
				DATA_cnt++;	
				if (DATA_cnt == NAND_PAGE_SIZE)
				{
					DATA_cnt = 0;
					current_state = COMMAND;
				}
			}
			break;
		case ERASE_DATA:
#ifdef	DEBUG_NAND
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> NAND_ERASE" << endl;
#endif
			for(int i=0; i<NAND_PAGE_SIZE*32; i++)
				FLASH_Array[flash_addr+i] = 0xFF;
			current_state = CONFIRM;
			break;
		case CONFIRM:
			if (cmd == NAND_START_PROGRAM)
			{
#ifdef	DEBUG_NAND
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> BUSY_count: " << BUSY_cnt_program << endl;
#endif
				if (BUSY_cnt_program != 0)
				{
					R_B = false;
					BUSY_cnt_program--;
				}
				else
				{
					R_B = true;
					BUSY_cnt_program = DEFAULT_PROGRAM_BUSY_CYCLE;
					current_state = READ_STATUS;
				}
			}
			else if (cmd == NAND_START_READ)
			{
#ifdef	DEBUG_NAND
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> BUSY_count: " << BUSY_cnt_read << endl;
#endif
				if (BUSY_cnt_read != 0)
				{
					R_B = false;
					BUSY_cnt_read--;
				}
				else
				{
					R_B = true;
					BUSY_cnt_read = DEFAULT_READ_BUSY_CYCLE; // Need to change
					current_state = RD_DATA;
				}
			}
			else if (cmd == NAND_START_ERASE)
			{
#ifdef	DEBUG_NAND
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> BUSY_count: " << BUSY_cnt_erase << endl;
#endif
				if (BUSY_cnt_erase != 0)
				{
					R_B = false;
					BUSY_cnt_erase--;
				}
				else
				{
					R_B = true;
					BUSY_cnt_erase = DEFAULT_ERASE_BUSY_CYCLE;
					current_state = READ_STATUS;
				}
			}
			break;
		case READ_STATUS:
#ifdef	DEBUG_NAND
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> NAND_READ_STATUS : 0x" << hex << (unsigned)DATA_IO << dec << endl;
#endif
			if (DATA_IO == NAND_READ_STATUS)
			{
				R_B = true;
				DATA_IO = NAND_IO_OK; // always OK!
				current_state = COMMAND;
			}
			break;
		default:
			cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> Unknown Command!" << endl;
			sc_stop();
			break;
		}
		wait();
	}
}